package com.lang.runtime;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**<pre>
 * {@link RuntimeDemo#executeBat(String, String)}调用后的内部具体实现
 * 具体实现也看产看{@link ProcessBuilder}
 * </pre>
 * @author Ackles
 */
public class ProcessBuilderDemo {

    /**
     * Create a process using the win32 function CreateProcess.
     * The method is synchronized due to MS kb315939 problem.
     * All native handles should restore the inherit flag at the end of call.
     *
     * @param cmdstr the Windows command line
     * @param envblock NUL-separated, double-NUL-terminated list of
     *        environment strings in VAR=VALUE form
     * @param dir the working directory of the process, or null if
     *        inheriting the current directory from the parent process
     * @param stdHandles array of windows HANDLEs.  Indexes 0, 1, and
     *        2 correspond to standard input, standard output and
     *        standard error, respectively.  On input, a value of -1
     *        means to create a pipe to connect child and parent
     *        processes.  On output, a value which is not -1 is the
     *        parent pipe handle corresponding to the pipe which has
     *        been created.  An element of this array is -1 on input
     *        if and only if it is <em>not</em> -1 on output.
     * @param redirectErrorStream redirectErrorStream attribute
     * @return the native subprocess HANDLE returned by CreateProcess
     */
    private static synchronized native long create(String cmdstr,
                                      String envblock,
                                      String dir,
                                      long[] stdHandles,
                                      boolean redirectErrorStream)
        throws IOException;

	public static void main(String[] args) throws IOException {
		SecurityManager securityManager = System.getSecurityManager();
		System.out.println(securityManager);
        String value = System.getProperty("jdk.lang.Process.allowAmbiguousCommands");
        System.out.println(value);
        String executablePath = getExecutablePath("cmd");
        System.out.println(executablePath);
        String[] cmd = {"cmd", "/c", "start", "cmd"};
		String commandLine = createCommandLine(VERIFICATION_LEGACY, executablePath, cmd);
		System.out.println(commandLine);
		
		List<String> cmdarray = new ArrayList<>();
		cmdarray.add("cmd");
		cmdarray.add("/c");
		cmdarray.add("start");
		cmdarray.add("cmd");
		File dir = new File("E:\\work\\zc\\cf\\server\\server_tw_dev\\centerserver");
		
		ProcessBuilder pb = new ProcessBuilder(cmdarray);
		pb.directory(dir);
        pb.start();
	}
	
	static String getExecutablePath(String path)
	        throws IOException
	    {
	        boolean pathIsQuoted = isQuoted(true, path,
	                "Executable name has embedded quote, split the arguments");
	        // Win32 CreateProcess requires path to be normalized
	        File fileToRun = new File(pathIsQuoted
	            ? path.substring(1, path.length() - 1)
	            : path);
	        // From the [CreateProcess] function documentation:
	        //
	        // "If the file name does not contain an extension, .exe is appended.
	        // Therefore, if the file name extension is .com, this parameter
	        // must include the .com extension. If the file name ends in
	        // a period (.) with no extension, or if the file name contains a path,
	        // .exe is not appended."
	        //
	        // "If the file name !does not contain a directory path!,
	        // the system searches for the executable file in the following
	        // sequence:..."
	        //
	        // In practice ANY non-existent path is extended by [.exe] extension
	        // in the [CreateProcess] funcion with the only exception:
	        // the path ends by (.)

	        return fileToRun.getPath();
	    }
	
	/**
	 * 判断字符串是否被在引号里
	 * @param noQuotesInside
	 * @param arg 待判断的字符串
	 * @param errorMessage
	 * @return
	 */
    static boolean isQuoted(boolean noQuotesInside, String arg, String errorMessage) {
        int lastPos = arg.length() - 1;
        if (lastPos >=1 && arg.charAt(0) == '"' && arg.charAt(lastPos) == '"') {
            // The argument has already been quoted.
            if (noQuotesInside) {
                if (arg.indexOf('"', 1) != lastPos) {
                    // There is ["] inside.
                    throw new IllegalArgumentException(errorMessage);
                }
            }
            return true;
        }
        if (noQuotesInside) {
            if (arg.indexOf('"') >= 0) {
                // There is ["] inside.
                throw new IllegalArgumentException(errorMessage);
            }
        }
        return false;
    }
    

    private static final int VERIFICATION_CMD_BAT = 0;
    private static final int VERIFICATION_WIN32 = 1;
    private static final int VERIFICATION_LEGACY = 2;
    private static final char ESCAPE_VERIFICATION[][] = {
            // We guarantee the only command file execution for implicit [cmd.exe] run.
            //    http://technet.microsoft.com/en-us/library/bb490954.aspx
            {' ', '\t', '<', '>', '&', '|', '^'},

            {' ', '\t', '<', '>'},
            {' ', '\t'}
        };
    
	static String createCommandLine(int verificationType, final String executablePath, final String cmd[]) {
		StringBuilder cmdbuf = new StringBuilder(80);
		cmdbuf.append(executablePath);
		for (int i = 1; i < cmd.length; ++i) {
			cmdbuf.append(' ');
			String s = cmd[i];
			if (needsEscaping(verificationType, s)) {
				cmdbuf.append('"').append(s);
				// The code protects the [java.exe] and console command line
				// parser, that interprets the [\"] combination as an escape
				// sequence for the ["] char.
				// http://msdn.microsoft.com/en-us/library/17w5ykft.aspx
				//
				// If the argument is an FS path, doubling of the tail [\]
				// char is not a problem for non-console applications.
				//
				// The [\"] sequence is not an escape sequence for the [cmd.exe]
				// command line parser. The case of the [""] tail escape
				// sequence could not be realized due to the argument validation
				// procedure.
				if ((verificationType != VERIFICATION_CMD_BAT) && s.endsWith("\\")) {
					cmdbuf.append('\\');
				}
				cmdbuf.append('"');
			} else {
				cmdbuf.append(s);
			}
		}
		return cmdbuf.toString();
	}
	
	static boolean needsEscaping(int verificationType, String arg) {
        // Switch off MS heuristic for internal ["].
        // Please, use the explicit [cmd.exe] call
        // if you need the internal ["].
        //    Example: "cmd.exe", "/C", "Extended_MS_Syntax"

        // For [.exe] or [.com] file the unpaired/internal ["]
        // in the argument is not a problem.
        boolean argIsQuoted = isQuoted(
            (verificationType == VERIFICATION_CMD_BAT),
            arg, "Argument has embedded quote, use the explicit CMD.EXE call.");

        if (!argIsQuoted) {
            char testEscape[] = ESCAPE_VERIFICATION[verificationType];
            for (int i = 0; i < testEscape.length; ++i) {
                if (arg.indexOf(testEscape[i]) >= 0) {
                    return true;
                }
            }
        }
        return false;
    }
}
